﻿using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Text;
using System.Windows.Forms;

namespace 串口助手
{
    public partial class Form1 : Form
    {

        //串口标志位
        private bool isOpen = false;

        //暂停标志位
        private bool isRxShow = true;

        //接收缓冲区
        private List<byte> reciveBuffer = new List<byte>();

        //发送缓冲区
        private List<byte> sendBuffer = new List<byte>();

        //接收计数
        private int reciveCount = 0;

        //发送计数
        private int sendCount = 0;
        
        //实例化SerialPort窗口事件
        public Form1()
        {
            InitializeComponent();

            Control.CheckForIllegalCrossThreadCalls = false;
        }

        //加载窗口控件事件
        private void Form1_Load(object sender, EventArgs e)
        {
            serialLoad();
        }

        //自定义私有方法，初始化串口配置等
        private void serialLoad()
        {

            EncodingInfo[] encodingInfos = Encoding.GetEncodings();


            RegistryKey keyCom = Registry.LocalMachine.OpenSubKey(@"Hardware\DeviceMap\SerialComm");

            string[] sSubKeys = keyCom.GetValueNames();
            port_cbb.Items.Clear();
            foreach (var sValue in   sSubKeys)
            {
                string portName = (string)keyCom.GetValue(sValue);
                port_cbb.Items.Add(portName);
            }

            this.port_cbb.SelectedIndex = 0;

            this.baud_cbb.SelectedIndex = 1;

            this.check_cbb.SelectedIndex = 0;

            this.databit_cbb.SelectedIndex = 3;

            this.stopbit_cbb.SelectedIndex = 0;
        }

        //发送
        private void sendData()
        {
            //写串口，发送缓冲区中的数据
            serialPort1.Write(sendBuffer.ToArray(),0,sendBuffer.Count);
            //累加发送数据长度
            sendCount += sendBuffer.Count;
            //更新数据长度到状态
            sencount_tssl.Text = sendCount.ToString();
        }

        private void send_btn_Click(object sender, EventArgs e)
        {
            // 如果发送的数据不为空，则发送
            if (this.send_rtb.Text != "" && serialPort1.IsOpen)
            {
                Console.WriteLine(Transform.ToHexString(sendBuffer.ToArray()));//调试用
                sendData(); //调用自己的发送方法
            }
            else
            {
                MessageBox.Show("请先输入发送数据！");

            }
        }

        //点击打开串口按钮
        private void open_btn_Click(object sender, EventArgs e)
        {
            try
            {
                if (serialPort1.IsOpen == false)
                {
                    //设置基础配置
                    serialPort1.PortName = port_cbb.Text;
                    serialPort1.BaudRate = Convert.ToInt32(baud_cbb.Text);
                    serialPort1.DataBits = Convert.ToInt32(databit_cbb.Text);
                    switch (check_cbb.SelectedIndex)
                    {
                        //  none  odd  even 
                        case 0:
                            serialPort1.Parity = Parity.None;
                            break;
                        case 1:
                            serialPort1.Parity = Parity.Odd;
                            break;
                        case 2:
                            serialPort1.Parity = Parity.Even;
                            break;
                        default:
                            serialPort1.Parity = Parity.None;
                            break;
                    }
                    switch (stopbit_cbb.SelectedIndex)
                    {
                        // 1 1.5 2
                        case 0:
                            serialPort1.StopBits = StopBits.One;
                            break;
                        case 1:
                            serialPort1.StopBits = StopBits.OnePointFive;
                            break;
                        case 2:
                            serialPort1.StopBits = StopBits.Two;
                            break;
                        default:
                            serialPort1.StopBits = StopBits.One;
                            break;
                    }
                    serialPort1.Open();
                    isOpen = true;
                    open_btn.Text = "关闭串口";
                }
                else
                {
                    serialPort1.Close();
                    isOpen = false;
                    open_btn.Text = "打开串口";

                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString()+serialPort1.PortName.ToString());
            }
        }


        //串口接收到数据事件
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //如果暂停了，那么就不显示在接收框，直接return
            if (isRxShow == false) return;

            // 1,需要读取有效的数据 BytesToRead
            //创建一个 数组，大小是当前串口缓存中的字节数量
            byte[] dataTemp = new byte[serialPort1.BytesToRead];
            //读取数据。
            serialPort1.Read(dataTemp,0,dataTemp.Length);
            //添加一组（集合）数据到接收框的末尾
            reciveBuffer.AddRange(dataTemp);
            //计数下接收到的数据
            reciveCount += dataTemp.Length;

            // 使用 Invoke 方法确保在 UI 线程上执行以下操作，防止异步线程
            this.Invoke(new EventHandler(delegate //委托给 UI 线程
            {
                // 显示接收数据的长度
                recivecount_tssl.Text = reciveCount.ToString();

                //如果没有勾选16进制
                if (!recivehex_chb.Checked)
                {
                    // 通过指定的编码格式来获取字符串
                    string str = Encoding.GetEncoding("gb2312").GetString(dataTemp);
                    // 3，0x00 -> \0 结束 不会显示，所以转义成\0 显示出来
                    str=str.Replace("\0", "\\0");

                    //向接收文本框追加处理后的数据
                    recive_rtb.AppendText(str);

                    //下面等同于上面三句话
                    //recive_rtb.AppendText(Encoding.GetEncoding("gb2312").GetString(dataTemp).Replace("\0", "\\0"));
                }
                else
                {
                    //  十六进制是选中的状态下
                    //向接收文本框追加  将byte转化为16进制的 数据，然后追加一个空格
                    recive_rtb.AppendText(Transform.ToHexString(dataTemp, " "));
                }

            }));
        }

        //暂停按键事件
        private void stop_btn_Click(object sender, EventArgs e)
        {
            if (isRxShow == true)
            {
                isRxShow = false;
                stop_btn.Text = "取消暂停";
            }
            else
            {
                isRxShow = true;
                stop_btn.Text = "暂停";
            }
        }

        //接收 16进制切换 框 点击
        private void recivehex_chb_CheckedChanged(object sender, EventArgs e)
        {
            //如果接收缓冲区 为空，直接退出
            if (recive_rtb.Text == "") return;
            //如果16进制为勾选状态
            if (recivehex_chb.Checked)
            {
                //把接收缓冲区的所有 变为 16进制， 用空格隔开
                recive_rtb.Text = Transform.ToHexString(reciveBuffer.ToArray()," ");
            }
            else
            {
                //将接收到的字节数据根据 GB2312 编码转换为字符串，替换不可见的空字符 \0 为可见的 \\0
                recive_rtb.Text = Encoding.GetEncoding("gb2312").GetString(reciveBuffer.ToArray()).Replace("\0","\\0");
            }
        }

        //清空接收的数据
        private void clear_btn_Click(object sender, EventArgs e)
        {
            reciveBuffer.Clear();
            recivecount_tssl.Text = "";
            recive_rtb.Text = "";
        }

        //自动清空点击事件
        private void autoclear_chb_CheckedChanged(object sender, EventArgs e)
        {
            //如果勾选了自动清空，那么启动定时器
            if (autoclear_chb.Checked)
            {
                timer1.Start();
            }
            else
            {
                timer1.Stop();
            }
        }

        //Timer定时事件到（100ms检测一次)
        private void timer1_Tick(object sender, EventArgs e)
        {
            //如果接收框内容大于4096，那么自动清除
            if (recive_rtb.Text.Length > 4096)
            {
                reciveBuffer.Clear();       //清除接收缓冲区
                recive_rtb.Text = "";       //清除接收框
                recivecount_tssl.Text = ""; //清除计数
            }
        }

        //当焦点移开发送框时触发
        private void send_rtb_Leave(object sender, EventArgs e)
        {
            //判断是否要发送16进制数据（是否为选中状态）
            if (sendhex_chb.CheckState == CheckState.Checked)
            {
                //选中了16进制发送

                //把所有空格删除后，判断这是不是一个16进制字符串
                if (DataEncoding.IsHexString(send_rtb.Text.Replace(" ", "")))
                {
                    sendBuffer.Clear();
                    //把转换好的数据，存放到发送缓冲区
                    sendBuffer.AddRange(Transform.ToBytes(send_rtb.Text.Replace(" ", "")));

                }
                else //不是16进制数据
                {
                    MessageBox.Show("请输入正确的十六进制数据！！", "发送框警告");
                    send_rtb.Select();
                }
            }
            else   //未选中16进制。
            {
                sendBuffer.Clear();
                //把当前数据，存放到发送缓冲区。
                sendBuffer.AddRange(Encoding.GetEncoding("gb2312").GetBytes(send_rtb.Text));
            }
        }

        //注意如果使用文本发生变化事件，去处理16进制的转换，会发生错误
        private void send_rtb_TextChanged(object sender, EventArgs e)
        {
            // 十六进制切换 会出现问题  这问题就是0x00 转换
        }

        //发送 16进制切换 框 点击
        private void sendhex_chb_CheckedChanged(object sender, EventArgs e)
        {
            //判断发送框文本你是否为空
            if (send_rtb.Text == "") return;

            //把发送缓冲区的所有 变为 16进制， 用空格隔开
            if (sendhex_chb.Checked == true)
            {
               
                send_rtb.Text = Transform.ToHexString(sendBuffer.ToArray(), " ");
            }
            else 
            { 
                send_rtb.Text = Encoding.GetEncoding("gb2312").GetString(sendBuffer.ToArray()).Replace("\0","\\0");

            }
        }

        //发送框清除按钮点击事件
        private void sendclear_btn_Click(object sender, EventArgs e)
        {
            sendBuffer.Clear();     //清除发送框缓冲区

            send_rtb.Text = "";     //清除发送框

            sendCount = 0;          //清除发送计数

            sencount_tssl.Text = "0";        //清除发送框计数
        }

    }
}
